﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using Anagramme.Contracts;

namespace Anagramme.Server.Service
{
    /// <summary>
    /// Classe de base pour les services
    /// </summary>
    /// <typeparam name="T">Type du callback associé</typeparam>
    public abstract class ServiceBase<T>
        where T : class
    {
        #region Fields
        private Guid _token;
        #endregion

        #region Properties
        /// <summary>
        /// Obtient le callback du client
        /// </summary>
        protected T ClientCallback
        {
            get
            {
                if (object.ReferenceEquals(OperationContext.Current, null))
                {
                    return null;
                }
                return OperationContext.Current.GetCallbackChannel<T>();
            }
        }

        /// <summary>
        /// Obtient la liste de tout les callbacks du serveur
        /// </summary>
        protected IEnumerable<T> ClientCallbacks
        {
            get
            {
                return CallbacksManager.Default.GetCallbacks<T>();
            }
        }

        /// <summary>
        /// Obtient le contexte de données du serveur
        /// </summary>
        protected ServerContext Context
        {
            get { return ServerContext.Current; }
        }

        /// <summary>
        /// Obtient la liste des callbacks du serveurs, excepté le callback du client
        /// </summary>
        protected IEnumerable<T> OthersClientCallbacks
        {
            get
            {
                return CallbacksManager.Default.GetOthersCallbacks<T>(this.Token);
            }
        }

        /// <summary>
        /// Obtient les informations du joueur associé au client
        /// </summary>
        protected Player Player
        { 
            get 
            { 
                return this.Context.Players.SingleOrDefault(p => p.Token.Equals(this.Token)); 
            }
        }

        /// <summary>
        /// Obtient le jeton de sécurité du client
        /// </summary>
        protected Guid Token
        {
            get { return _token; }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Obtient le joueur associé à un callback passé au paramètre
        /// </summary>
        /// <param name="callback">Callback associé au joueur</param>
        /// <returns>Retourne le joueur associé</returns>
        protected Player GetPlayerFromCallback(T callback)
        {
            Guid token = CallbacksManager.Default.GetToken<T>(callback);
            Player player = this.Context.Players.Single(p => p.Token.Equals(token));
            return player;
        }
        
        /// <summary>
        /// Authentifie le client sur le serveur
        /// </summary>
        /// <param name="token">Jeton de sécurité du client</param>
        protected void LogOn(Guid token)
        {
            this._token = token;
            CallbacksManager.Default.Register<T>(token, this.ClientCallback);
            OperationContext.Current.Channel.Closing += Channel_Closing;
            OperationContext.Current.Channel.Closed += Channel_Closed;
        }

        /// <summary>
        /// Déconnecte le client du serveur
        /// </summary>
        protected void LogOff()
        {
            if (!object.ReferenceEquals(this.ClientCallback, null))
            {
                CallbacksManager.Default.Unregister<T>(this.ClientCallback);
            }
        }

        /// <summary>
        /// Méthode soulevée lorsque le callback du client passe à l'état Closed
        /// </summary>
        protected virtual void OnChannelClosed()
        {

        }

        /// <summary>
        /// Méthode soulevée lorsque le callback du client passe à l'état Closing
        /// </summary>
        protected virtual void OnChannelClosing()
        {

        }
        #endregion

        #region Event Handlers
        private void Channel_Closing(object sender, EventArgs e)
        {
            this.OnChannelClosing();
        }

        private void Channel_Closed(object sender, EventArgs e)
        {
            IEnumerable<object> callbacks = CallbacksManager.Default.GetCallbacks(this.Token);
            if (object.ReferenceEquals(callbacks, null) || callbacks.Count().Equals(0))
            {
                Player player = this.Player;
                if (Context.Players.Contains(player))
                {
                    Context.Players.Remove(player);
                }
                this._token = Guid.Empty;
            }
            this.OnChannelClosed();
        }
        #endregion
    }
}
